﻿namespace wToggl
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Net.Http;
    using System.Runtime.CompilerServices;
    using System.Threading.Tasks;

    using Windows.UI.Core;
    using Windows.UI.Popups;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Navigation;

    using wToggl.Common;

    using wToggle.Platform;
    using wToggle.Platform.Annotations;
    using wToggle.Platform.Data;
    using wToggle.Platform.Interfaces;

    using XPlatUtils;

    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class WorkspacePage : INotifyPropertyChanged
    {
        private NavigationHelper navigationHelper;

        private object isWorking;

        private string overlayMessage;

        private IEnumerable<Project> projects;

        private IEnumerable<Client> clients;

        private IEnumerable<Tag> tags;

        public WorkspacePage()
        {
            this.InitializeComponent();

            this.navigationHelper = new NavigationHelper(this);
            this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
            this.navigationHelper.SaveState += this.NavigationHelper_SaveState;
        }

        private void NavigationHelper_SaveState(object sender, SaveStateEventArgs e)
        {
            if (e.PageState == null)
            {
                return;
            }

            e.PageState["page"] = this.Hub.Sections.IndexOf(this.Hub.SectionsInView.First());
        }

        private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
        {
            if (e.NavigationParameter == null)
            {
                throw new InvalidOperationException("Empty navigation parameter passed to TimeEntryDetailPage");
            }

            if (!(e.NavigationParameter is ulong))
            {
                throw new ArgumentException("Navigation argument passed to TimeEntryDetailPage has incorrect type");
            }

            var settings = ServiceContainer.Resolve<IApplicationSettings>();
            var auth = ServiceContainer.Resolve<ITogglAuth>();

            if (!settings.HasLogin() || await auth.Authenticate(settings.Username, settings.Password) != AuthStatus.Success)
            {
                await this.Dispatcher.RunAsync(
                    CoreDispatcherPriority.Normal,
                    () =>
                    {
                        this.Frame.Navigate(typeof(LoginPage));
                    });

                return;
            }

            await this.LoadWorkspace((ulong)e.NavigationParameter);

            if (e.PageState == null)
            {
                return;
            }

            if (e.PageState.ContainsKey("page"))
            {
                this.Hub.ScrollToSection(this.Hub.Sections[(int)e.PageState["page"]]);
            }
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            Analytics.ReportPageVisit(this);  
            this.navigationHelper.OnNavigatedTo(e);
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            this.navigationHelper.OnNavigatedFrom(e);
        }

        private async Task LoadWorkspace(ulong id, bool silent = false)
        {
            var provider = ServiceContainer.Resolve<IWorkspaceProvider>();

            try
            {
                this.IsWorking = true;
                this.Workspace = await provider.GetWorkspace(id);

                if (this.Workspace != null)
                {
                    await this.ReloadWorkspace();
                }
                else
                {
                    if (!silent)
                    {
                        this.Dispatcher.ShowMessage(
                            "Time entry could not be loaded. Verify your internet connectivity and try again.",
                            "Toggly");
                    }

                    this.Projects = null;
                    this.Tags = null;
                    this.Clients = null;
                }
            }
            finally
            {
                this.IsWorking = false;
            }
        }

        private async Task ReloadWorkspace()
        {
            await this.Workspace.LoadWorkspaceData();

            foreach (var p in this.Workspace.Projects)
            {
                await p.LoadClient();
            }

            this.Projects = this.Workspace.Projects.OrderBy(p => p.Name).ToList();
            this.Tags = this.Workspace.Tags.OrderBy(t => t.Name).ToList();
            this.Clients = this.Workspace.Clients.OrderBy(c => c.Name).ToList();
        }

        public Workspace Workspace { get; set; }

        public object IsWorking
        {
            get
            {
                return this.isWorking;
            }

            set
            {
                this.isWorking = value;
                this.OnPropertyChanged();
            }
        }

        public string OverlayMessage
        {
            get
            {
                return this.overlayMessage;
            }
            set
            {
                this.overlayMessage = value;
                this.OnPropertyChanged();
            }
        }

        public IEnumerable<Project> Projects
        {
            get
            {
                return this.projects;
            }
            set
            {
                this.projects = value;
                this.OnPropertyChanged();
            }
        }

        public IEnumerable<Client> Clients
        {
            get
            {
                return this.clients;
            }
            set
            {
                this.clients = value;
                this.OnPropertyChanged();
            }
        }

        public IEnumerable<Tag> Tags
        {
            get
            {
                return this.tags;
            }
            set
            {
                this.tags = value;
                this.OnPropertyChanged();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            this.Dispatcher.RunAsync(
                CoreDispatcherPriority.Normal,
                () =>
                    {
                        var handler = this.PropertyChanged;
                        if (handler != null)
                        {
                            handler(this, new PropertyChangedEventArgs(propertyName));
                        }
                    });
        }

        private void AddItemButton_OnClick(object sender, RoutedEventArgs e)
        {
            if (this.Workspace == null)
            {
                this.Dispatcher.ShowMessage("Workspace could not be laoded - modifications are disabled.");
                return;
            }

            switch (this.Hub.Sections.IndexOf(this.Hub.SectionsInView.First()))
            {
                case 0:
                    this.Frame.Navigate(typeof(ProjectPage), "w" + this.Workspace.Id);
                    break;
                case 1:
                    this.Frame.Navigate(typeof(ClientPage), "w" + this.Workspace.Id);
                    break;
                case 2:
                    this.Frame.Navigate(typeof(TagPage), "w" + this.Workspace.Id);
                    break;
            }
        }

        private void ProjectDetailsMenu_OnClick(object sender, RoutedEventArgs e)
        {
            var project = sender.GetDataContext<Project>();

            if (project == null)
            {
                return;
            }

            this.Frame.Navigate(typeof(ProjectPage), "p" + project.Id);
        }

        private async void ProjectDeleteMenu_OnClick(object sender, RoutedEventArgs e)
        {
            var menuItem = sender as MenuFlyoutItem;

            if (menuItem == null)
            {
                return;
            }

            var entry = (Project)menuItem.DataContext;
            var md = new MessageDialog("Do you really wish to delete the selected project?", "Delete project?");
            md.Commands.Add(
                new UICommand(
                    "yes",
                     async o =>
                     {
                         try
                         {
                             var provider = ServiceContainer.Resolve<IProjectProvider>();

                             if (await provider.Delete(entry.Id))
                             {
                                 CacheLayer.UpdateProjectReferences(entry.Id, 0);
                             }

                             await this.ReloadWorkspace();
                         }
                         catch (HttpRequestException)
                         {
                             this.Dispatcher.ShowMessage(
                                 "Attempt to delete the specified project failed. Check your internet connection and try again.",
                                 "Toggly");
                         }
                     }));
            md.Commands.Add(new UICommand("no"));
            md.CancelCommandIndex = 1;
            md.DefaultCommandIndex = 1;
            await md.ShowAsync();
        }

        private void ProjectList_OnTapped(object sender, TappedRoutedEventArgs e)
        {
            var project = sender.GetDataContext<Project>();

            if (project == null)
            {
                return;
            }

            this.Frame.Navigate(typeof(ProjectPage), "p" + project.Id);
        }

        private void ProjectList_OnHolding(object sender, HoldingRoutedEventArgs e)
        {
            var obj = sender as FrameworkElement;

            if (obj == null)
            {
                return;
            }

            FlyoutBase.ShowAttachedFlyout(obj); 
        }

        private void ClientDetailsMenu_OnClick(object sender, RoutedEventArgs e)
        {
            var client = sender.GetDataContext<Client>();

            if (client == null)
            {
                return;
            }

            this.Frame.Navigate(typeof(ClientPage), "c" + client.Id);
        }

        private async void ClientDeleteMenu_OnClick(object sender, RoutedEventArgs e)
        {
            var menuItem = sender as MenuFlyoutItem;

            if (menuItem == null)
            {
                return;
            }

            var entry = (Client)menuItem.DataContext;
            var md = new MessageDialog("Do you really wish to delete the selected client?", "Delete client?");
            md.Commands.Add(
                new UICommand(
                    "yes",
                     async o =>
                     {
                         try
                         {
                             var provider = ServiceContainer.Resolve<IClientProvider>();
                             await provider.Delete(entry.Id);

                             await this.ReloadWorkspace();
                         }
                         catch (HttpRequestException)
                         {
                             this.Dispatcher.ShowMessage(
                                 "Attempt to delete the specified client failed. Check your internet connection and try again.",
                                 "Toggly");
                         }
                     }));
            md.Commands.Add(new UICommand("no"));
            md.CancelCommandIndex = 1;
            md.DefaultCommandIndex = 1;
            await md.ShowAsync();
        }

        private void ClientList_OnHolding(object sender, HoldingRoutedEventArgs e)
        {
            var obj = sender as FrameworkElement;

            if (obj == null)
            {
                return;
            }

            FlyoutBase.ShowAttachedFlyout(obj); 
        }

        private void ClientList_OnTapped(object sender, TappedRoutedEventArgs e)
        {
            var client = sender.GetDataContext<Client>();

            if (client == null)
            {
                return;
            }

            this.Frame.Navigate(typeof(ClientPage), "c" + client.Id);
        }

        private void TagList_OnTapped(object sender, TappedRoutedEventArgs e)
        {
            this.NavigateToTag(sender);
        }

        private void NavigateToTag(object sender)
        {
            if (this.Workspace == null)
            {
                this.Dispatcher.ShowMessage(
                    "No workspace loaded. Verify your internet connection and reload.",
                    "Toggly");
                return;
            }

            var client = sender.GetDataContext<Tag>();

            if (client == null)
            {
                return;
            }

            this.Frame.Navigate(typeof(TagPage), "t" + client.Id + "|" + this.Workspace.Id);
        }

        private void TagList_OnHolding(object sender, HoldingRoutedEventArgs e)
        {
            var obj = sender as FrameworkElement;

            if (obj == null)
            {
                return;
            }

            FlyoutBase.ShowAttachedFlyout(obj); 
        }

        private void TagDetailsMenu_OnClick(object sender, RoutedEventArgs e)
        {
            this.NavigateToTag(sender);
        }

        private async void TagDeleteMenu_OnClick(object sender, RoutedEventArgs e)
        {
            var menuItem = sender as MenuFlyoutItem;

            if (menuItem == null)
            {
                return;
            }

            var entry = (Tag)menuItem.DataContext;
            var md = new MessageDialog("Do you really wish to delete the selected tag?", "Delete tag?");
            md.Commands.Add(
                new UICommand(
                    "yes",
                     async o =>
                     {
                         try
                         {
                             var provider = ServiceContainer.Resolve<ITagProvider>();
                             var result = await provider.Delete(entry.Id);

                             if (result)
                             {
                                 CacheLayer.UpdateTagReferences(entry.Name, null);
                             }

                             await this.ReloadWorkspace();
                         }
                         catch (HttpRequestException)
                         {
                             this.Dispatcher.ShowMessage(
                                 "Attempt to delete the specified tag failed. Check your internet connection and try again.",
                                 "Toggly");
                         }
                     }));
            md.Commands.Add(new UICommand("no"));
            md.CancelCommandIndex = 1;
            md.DefaultCommandIndex = 1;
            await md.ShowAsync();
        }
    }
}
